﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Configuration.Provider;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web.Providers;
using System.Web.Security;
using System.Web.Services;
using T2.WebSecurity.Business;
using T2.WebSecurity.Business.Entities;
using T2.WebSecurity.Business.Entities.Interfaces;
using T2.WebSecurity.Business.Entities.Search;
using T2.WebSecurity.Business.Entities.Search.Interfaces;

namespace T2.WebSecurity
{
    public class SimpleRoleProvider : RoleProvider
    {

        private IBusinessService bs = new BusinessService();

        public SimpleRoleProvider()
        {
            //Configuration is handled in the Initialize method below
        }

        public override void Initialize(string name, NameValueCollection config)
        {
            if (config == null)
                throw new ArgumentNullException("config");
            if (string.IsNullOrEmpty(name))
                name = "TTSSimpleRoleProvider";
            if (string.IsNullOrEmpty(config["description"]))
            {
                config.Remove("description");
                config.Add("description", "T Squared Software, Inc. role provider");
            }
            base.Initialize(name, config);

            ApplicationName = GetValueOrDefault(config, "applicationName", o => o.ToString(), "MySampleApp");

            this.ConnectionStringName = GetValueOrDefault(config, "connectionStringName", o => o.ToString(), string.Empty);
            //adapterFactory.Initialize(ConnectionStringName);

            config.Remove("name");
            config.Remove("description");
            config.Remove("applicationName");
            config.Remove("connectionStringName");

            if (config.Count <= 0)
                return;
            var key = config.GetKey(0);
            if (string.IsNullOrEmpty(key))
                return;

            throw new ProviderException(string.Format(CultureInfo.CurrentCulture,
                                                      "The role provider does not recognize the configuration attribute {0}.",
                                                      key));
        }

        private static T GetValueOrDefault<T>(NameValueCollection nvc, string key, Func<object, T> converter, T defaultIfNull)
        {
            var val = nvc[key];

            if (val == null)
                return defaultIfNull;

            try
            {
                return converter(val);
            }
            catch
            {
                return defaultIfNull;
            }
        }

        public string ConnectionStringName { get; set; }






        public override void AddUsersToRoles(string[] usernames, string[] roleNames)
        {
            //This will add all of the users in usernames to all of the roles in roleNames
            List<IUserProfile_biz> userList = new List<IUserProfile_biz>();
            List<IApplicationRoles_biz> roleList = new List<IApplicationRoles_biz>();
            IApplication_biz app = this.GetApplicationByName(this.applicationName);
            foreach (string user in usernames)
            {
                IUserProfile_biz tempUser = this.GetUserByName(user);
                foreach (string role in roleNames)
                {
                    IApplicationRoles_biz tempRole = this.GetRoleByName(role);
                    //Now that we have both we can go ahead and add them to the UsersInRoles object
                    IUsersInRoles_biz userRole = new UsersInRoles_biz();
                    userRole.Action = "Add";
                    userRole.ApplicationRoleId = tempRole.ApplicationRoleId;
                    userRole.UserId = tempUser.UserId;
                    userRole.CreatedBy = "System";
                    userRole.CreatedDt = System.DateTime.Now;
                    userRole.ModifiedBy = "System";
                    userRole.ModifiedDt = System.DateTime.Now;
                    IDictionary<string, int> r = bs.UsersInRolesController_Set(userRole);
                    var ret = r.ToList().Select(s  => new {Key=s.Key, Value = s.Value}) ;
                    Int32 retVal = ret.Where(z => z.Key == "Return_Value").Select(m => m.Value).FirstOrDefault();
                    if(retVal <= 0)
                    {
                        throw new ProviderException("Role and User association was not set");
                    }
                    
                }
            }
            


        }

        private string applicationName = "/";
        public override string ApplicationName
        {
            get
            {
                return applicationName;
            }
            set
            {
                applicationName = value;
            }
        }

        public override void CreateRole(string roleName)
        {
            ///This will create a new role
            IApplication_biz app = this.GetApplicationByName(this.applicationName);
            IApplicationRoles_biz checkRoleExists = this.GetRoleByName(roleName);
            if (checkRoleExists != null)
            { 
                throw new ProviderException("Role Exists");
            }
            IApplicationRoles_biz param = new ApplicationRoles_biz();
            param.Action = "Add";
            param.ApplicationID = app.ApplicationID;
            param.CreatedBy = "System";
            param.CreatedDt = System.DateTime.Now;
            param.ModifiedBy = "System";
            param.ModifiedDt = System.DateTime.Now;
            param.RoleName = roleName;
            IDictionary<string, int> r = bs.ApplicationRolesController_Set(param);
            var ret = r.ToList().Select(s => new { Key = s.Key, Value = s.Value });
            Int32 retVal = ret.Where(z => z.Key == "Return_Value").Select(m => m.Value).FirstOrDefault();
            if (retVal <= 0)
            {
                throw new ProviderException("The new role  was not added");
            }
        }

        public override bool DeleteRole(string roleName, bool throwOnPopulatedRole)
        {
            //This will remove a role and all of its associations unless throwOnPopulatedRole is set to true
            //then it will simply throw an exception if the role is associated with a user
            ///This will create a new role
            //IApplication_biz app = this.GetApplicationByName(this.applicationName);
            IApplicationRoles_biz checkRoleExists = this.GetRoleByName(roleName);
            if (checkRoleExists == null)
            {
                throw new ProviderException("Role Does Not Exist and therefore the system cannot delete it");
            }
            if (checkRoleExists.UsersInRoles_List != null && checkRoleExists.UsersInRoles_List.Count > 0 && throwOnPopulatedRole)
            {
                throw new ProviderException("This role cannot be deleted since it has User accounts attached to it.");
            }
            foreach (IUsersInRoles_biz item in checkRoleExists.UsersInRoles_List)
            {
                item.Action = "Remove";
                IDictionary<string,int> cr = bs.UsersInRolesController_Set(item);
                var ret = cr.ToList().Select(s => new { Key = s.Key, Value = s.Value });
                Int32 childRetVal = ret.Where(z => z.Key == "Return_Value").Select(m => m.Value).FirstOrDefault();
                if (childRetVal <= 0)
                {
                    throw new ProviderException("UsersInRoles could not be deleted. Process teminated");
                }
            }
            checkRoleExists.Action = "Remove";
            IDictionary<string,int> r = bs.ApplicationRolesController_Set(checkRoleExists);
            var ret1 = r.ToList().Select(s => new { Key = s.Key, Value = s.Value });
            Int32 retVal = ret1.Where(z => z.Key == "Return_Value").Select(m => m.Value).FirstOrDefault();
            if (retVal <= 0)
            {
                throw new ProviderException("The role  was not deleted");
            }
            return true;
        }

        public override string[] FindUsersInRole(string roleName, string usernameToMatch)
        {
            //This will return an array of UserNames found for the role
            IApplicationRoles_biz appRole = this.GetRoleByName(roleName);
            IUserProfile_biz appUser = this.GetUserByName(usernameToMatch);
            List<string> retVal = new List<string>();
            if(appRole == null)
            {
                throw new ProviderException("Role not found");
            }
            if(appUser == null)
            {
                throw new ProviderException("User not found");
            }
            foreach (IUsersInRoles_biz item in appRole.UsersInRoles_List)
            {
                if (item.UserId == appUser.UserId)
                {
                    retVal.Add(appUser.UserName);
                }
            }
            
            return retVal.ToArray<string>();
        }

        public override string[] GetAllRoles()
        {
            //This will return an array of all roles for the application
            IApplicationRoles_biz param = new ApplicationRoles_biz();
            param.Action = "GetAll";
            var retValTemp = bs.ApplicationRolesController_Get(param, false, false);
            var retVal = retValTemp.Select(p => p.RoleName).ToArray<string>();
            return retVal;
        }

        public override string[] GetRolesForUser(string username)
        {
            //This will return an array of RoleNames for a given user by userName
            IUserProfile_biz userParam = this.GetUserByName(username);
            List<string> retVal = new List<string>();
            if (userParam == null)
            {
                throw new ProviderException("User not found");
            }
            foreach (IUsersInRoles_biz item in userParam.UsersInRoles_List)
            {
                IApplicationRoles_biz tempRole = this.GetRoleRoleID(item.ApplicationRoleId);
                if (tempRole == null)
                {
                    throw new ProviderException("Role not found in association with user");
                }
                retVal.Add(tempRole.RoleName);
            }
            
            return retVal.ToArray<string>();
        }

        public override string[] GetUsersInRole(string roleName)
        {
            //This will return an array of UserNames found for the role
            IApplicationRoles_biz appRole = this.GetRoleByName(roleName);
            
            List<string> retVal = new List<string>();
            if (appRole == null)
            {
                throw new ProviderException("Role not found");
            }
            
            foreach (IUsersInRoles_biz item in appRole.UsersInRoles_List)
            {
                IUserProfile_biz userTemp = this.GetUserByUserID(item.UserId);
                if (userTemp == null)
                {
                    throw new ProviderException("User not found from role association");
                }
                retVal.Add(userTemp.UserName);
                
            }

            return retVal.ToArray<string>();
        }

        public override bool IsUserInRole(string username, string roleName)
        {
            //This will return an array of UserNames found for the role
            IApplicationRoles_biz appRole = this.GetRoleByName(roleName);
            IUserProfile_biz appUser = this.GetUserByName(username);
            List<string> retVal = new List<string>();
            if (appRole == null)
            {
                throw new ProviderException("Role not found");
            }
            if (appUser == null)
            {
                throw new ProviderException("User not found");
            }
            foreach (IUsersInRoles_biz item in appRole.UsersInRoles_List)
            {
                if (item.UserId == appUser.UserId)
                {
                    retVal.Add(appUser.UserName);
                }
            }

            return Convert.ToBoolean(retVal.Count);
        }

        public override void RemoveUsersFromRoles(string[] usernames, string[] roleNames)
        {
            foreach (string tempUser in usernames)
            {
                IUserProfile_biz userTemp = this.GetUserByName(tempUser);
                if (userTemp != null)
                {
                    foreach (IUsersInRoles_biz myUserInRole in userTemp.UsersInRoles_List)
                    {
                        myUserInRole.Action = "Remove";
                        IDictionary<string,int> r = bs.UsersInRolesController_Set(myUserInRole);
                        var ret = r.ToList().Select(s => new { Key = s.Key, Value = s.Value });
                        Int32 retVal = ret.Where(z => z.Key == "Return_Value").Select(m => m.Value).FirstOrDefault();
                    }
                }
            }
            // Given all of the users in usernames this method will delete the association to all roles in roleNames

        }

        public override bool RoleExists(string roleName)
        {
            //This merely checks to see if the role exists
            IApplicationRoles_biz retVal = this.GetRoleByName(roleName);

            if (retVal != null)
            {
                return true;
            }
            return false;
        }

        private IUserProfile_biz GetUserByName(string username)
        {
            IUserProfileSearch_biz paramSearch = new UserProfileSearch_biz();
            paramSearch.Action = "Search";
            paramSearch.UserName = username;
            paramSearch.LastName = string.Empty;
            paramSearch.FirstName = string.Empty;
            paramSearch.PageNumber = 1;
            paramSearch.PageSize = 10;
            paramSearch.SortColumn = "UserProfileID";
            paramSearch.SortType = "asc";
            List<IUserProfile_biz> retVal = bs.UserProfileController_Search(paramSearch, true, true);
            if (retVal.Count > 0)
            {
                return retVal.FirstOrDefault();
            }
            else
            {
                return null;
            }
        }

        private IUserProfile_biz GetUserByUserID(Int32 userID)
        {
            IUserProfile_biz param = new UserProfile_biz();
            param.Action = "GetOne";
            param.UserId = userID;
            param.PageNumber = 1;
            param.PageSize = 10;
            param.SortColumn = "ApplicationID";
            param.SortType = "asc";
            List<IUserProfile_biz> retVal = bs.UserProfileController_Get(param, true, true);
            if (retVal.Count == 0)
            {
                return null;
            }
            else
            {
                return retVal.FirstOrDefault();
            }

        }

        private IApplicationRoles_biz GetRoleByName(string roleName)
        {
            IApplicationRolesSearch_biz paramSearch = new ApplicationRolesSearch_biz();
            paramSearch.Action = "Search";
            paramSearch.RoleName = roleName;
            paramSearch.PageNumber = 1;
            paramSearch.PageSize = 10;
            paramSearch.SortColumn = "ApplicationRoleId";
            paramSearch.SortType = "asc";
            List<IApplicationRoles_biz> retVal = bs.ApplicationRolesController_Search(paramSearch, true, true);
            if (retVal.Count > 0)
            {
                return retVal.FirstOrDefault();
            }
            else
            {
                return null;
            }
        }

        private IApplicationRoles_biz GetRoleRoleID(Int32  roleid)
        {
            IApplicationRoles_biz paramSearch = new ApplicationRoles_biz();
            paramSearch.Action = "GetOne";
            paramSearch.ApplicationRoleId = roleid;
            paramSearch.PageNumber = 1;
            paramSearch.PageSize = 10;
            paramSearch.SortColumn = "ApplicationRoleId";
            paramSearch.SortType = "asc";
            List<IApplicationRoles_biz> retVal = bs.ApplicationRolesController_Get(paramSearch, true, true);
            if (retVal.Count > 0)
            {
                return retVal.FirstOrDefault();
            }
            else
            {
                return null;
            }
        }

        private IApplication_biz GetApplicationByName(string appName)
        {
            IApplicationSearch_biz searchParam = new ApplicationSearch_biz();
            searchParam.Action = "Search";
            searchParam.ApplicationName = appName;
            searchParam.PageNumber = 1;
            searchParam.PageSize = 10;
            searchParam.SortColumn = "ApplicationID";
            searchParam.SortType = "asc";
            List<IApplication_biz> retVal = bs.ApplicationController_Search(searchParam, false, false);
            if (retVal.Count == 0)
            {
                return null;
            }
            else
            {
                return retVal.FirstOrDefault();
            }
        }
    }
}
